package com.hfzy.ihk.common.core.elasticsearch.base.job;

import com.alibaba.fastjson.JSON;
import com.hfzy.ihk.common.core.elasticsearch.ElasticsearchLocalCache;
import com.hfzy.ihk.common.core.elasticsearch.ElasticsearchOperation;
import com.hfzy.ihk.common.core.elasticsearch.EsQueryUtils;
import com.hfzy.ihk.common.core.elasticsearch.base.VersionCtrlHandler;
import com.hfzy.ihk.common.core.elasticsearch.entity.VersionCtrlErrorMsg;
import com.hfzy.ihk.common.core.elasticsearch.enums.VersionOperation;
import com.hfzy.ihk.common.core.elasticsearch.repository.common.VersionCtrlErrorMsgRepository;
import com.hfzy.ihk.common.facade.elasticsearch.ElasticsearchTableOperation;
import com.hfzy.ihk.common.facade.elasticsearch.entity.ElasticsearchTable;
import com.hfzy.ihk.common.support.aop.AspectjHelper;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2018/5/28.
 * 基础job，每个服务都需运行
 * @author wws
 */
@Component
@DependsOn({"versionCtrlErrorMsgRepository"})
@ConditionalOnProperty(prefix = "linkcall.elasticsearch",name = "enable")
public class CommonRetryVersionMsgJobs {

    private static final Logger logger = LoggerFactory.getLogger(CommonRetryVersionMsgJobs.class);

    @Autowired
    DeleteVersionHandler deleteVersionHandler;
    @Autowired
    RollbackVersionHandler rollbackVersionHandler;
    @Autowired
    UpdateVersionHandler updateVersionHandler;
    @Autowired
    VersionCtrlErrorMsgRepository versionCtrlErrorMsgRepository;

    @PostConstruct
    public void init() throws Exception {
        logger.info("init CommonRetryVersionMsgJobs bean");
    }

    @Scheduled(cron = "0 0/1 * * * ?")
    public void reTryVersionCtrl(){

        logger.debug("-----retry version-------");

       Page<VersionCtrlErrorMsg> page = versionCtrlErrorMsgRepository.findAll(PageRequest.of(0,1000));

        List<VersionCtrlErrorMsg> rmList = new ArrayList<>();
       page.getContent().stream().forEach(versionCtrlErrorMsg->{

           if(dealVersionCtrlMsg(versionCtrlErrorMsg)) {
               rmList.add(versionCtrlErrorMsg);
           }

       });

        rmList.forEach(msg-> versionCtrlErrorMsgRepository.delete(msg));
        logger.debug("-----retry version-------over,size:{}",rmList.size());

    }

    @Scheduled(cron = "0/10 * * * * ?")
    public void reTryCacheVersionCtrl(){

        logger.debug("-----retry reTryCacheVersionCtrl-------");

        if(ElasticsearchLocalCache.getVersionErrorMsgMemoryCache().size()<=0) {
            return;
        }

        List<VersionCtrlErrorMsg> rmList = new ArrayList<>();
        ElasticsearchLocalCache.getVersionErrorMsgMemoryCache().forEach(versionCtrlErrorMsg->{

           if(dealVersionCtrlMsg(versionCtrlErrorMsg)) {
               rmList.add(versionCtrlErrorMsg);
           }

        });

        rmList.forEach(msg-> ElasticsearchLocalCache.remove(msg));
        logger.debug("-----retry version-------over,size:{}",rmList.size());

    }

    protected boolean dealVersionCtrlMsg(VersionCtrlErrorMsg msg){
        boolean rs=false;
        switch (msg.getVersionOpt()){
            case "deleteVersion":
                rs = deleteVersionHandler.handle(msg);
                break;
            case "rollbackVersion":
                rs = rollbackVersionHandler.handle(msg);
                break;
            case "updateRedisVersion":
                rs = updateVersionHandler.handle(msg);
                break;
            default:
        }

        return rs;
    }

    @Component
    class DeleteVersionHandler implements VersionCtrlHandler{

        @Autowired
        RedisTemplate redisTemplate;

        @Override
        public boolean handle(VersionCtrlErrorMsg versionCtrlErrorMsg) {

            try {
                redisTemplate.opsForHash().delete(versionCtrlErrorMsg.getRedisKey(),versionCtrlErrorMsg.getDataId());
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }

            logger.debug("deleteVersion,versionKey:{},dataId:{}",versionCtrlErrorMsg.getRedisKey(),versionCtrlErrorMsg.getDataId());
            return true;
        }

    }

    @Component
    class RollbackVersionHandler implements VersionCtrlHandler{

        @Autowired
        RedisTemplate redisTemplate;

        @Override
        public boolean handle(VersionCtrlErrorMsg versionCtrlErrorMsg) {

            try {
                redisTemplate.opsForHash().increment(versionCtrlErrorMsg.getRedisKey(),
                        versionCtrlErrorMsg.getDataId(),
                        //回滚，使用负数
                        -versionCtrlErrorMsg.getRollbackValue());
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            logger.debug("rollbackVersion,versionKey:{},dataId:{}",versionCtrlErrorMsg.getRedisKey(),versionCtrlErrorMsg.getDataId());
            return true;
        }
    }

    @Component
    class UpdateVersionHandler implements VersionCtrlHandler{

        @Autowired
        RedisTemplate redisTemplate;
        @Autowired
        ElasticsearchTemplate elasticsearchTemplate;

        @Override
        public boolean handle(VersionCtrlErrorMsg versionCtrlErrorMsg) {

            Class clazz = null;
            try {
                //必须完整类名
                //TODO: 性能？
                clazz = Class.forName(versionCtrlErrorMsg.getOptTable());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return false;
            }

            GetQuery getQuery = new GetQuery();
            getQuery.setId(versionCtrlErrorMsg.getDataId());

            Object object = elasticsearchTemplate.queryForObject(getQuery,clazz);

           Long dbVersion= null;
            try {
                Field field =  clazz.getDeclaredField("version");
                field.setAccessible(true);
                dbVersion =  field.getLong(object);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
                return false;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return false;
            }

            try {
                redisTemplate.opsForHash().put(versionCtrlErrorMsg.getRedisKey(),versionCtrlErrorMsg.getDataId(),dbVersion);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            logger.debug("rollbackVersion,versionKey:{},dataId:{},version:{}",versionCtrlErrorMsg.getRedisKey(),versionCtrlErrorMsg.getDataId(),dbVersion);
            return true;
        }
    }
}
